home *** CD-ROM | disk | FTP | other *** search
/ InfoMagic Internet Tools 1993 July / Internet Tools.iso / RockRidge / mail / pine / imap-3.0 / ANSI / c-client / dawz.c < prev    next >
Encoding:
C/C++ Source or Header  |  1993-07-01  |  44.5 KB  |  1,527 lines

  1. /*
  2.  * Program:    Dawz mail routines
  3.  *
  4.  * Author:    Mark Crispin
  5.  *        Networks and Distributed Computing
  6.  *        Computing & Communications
  7.  *        University of Washington
  8.  *        Administration Building, AG-44
  9.  *        Seattle, WA  98195
  10.  *        Internet: MRC@CAC.Washington.EDU
  11.  *
  12.  * Date:    24 June 1992
  13.  * Last Edited:    1 July 1993
  14.  *
  15.  * Copyright 1993 by the University of Washington
  16.  *
  17.  *  Permission to use, copy, modify, and distribute this software and its
  18.  * documentation for any purpose and without fee is hereby granted, provided
  19.  * that the above copyright notice appears in all copies and that both the
  20.  * above copyright notice and this permission notice appear in supporting
  21.  * documentation, and that the name of the University of Washington not be
  22.  * used in advertising or publicity pertaining to distribution of the software
  23.  * without specific, written prior permission.  This software is made
  24.  * available "as is", and
  25.  * THE UNIVERSITY OF WASHINGTON DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED,
  26.  * WITH REGARD TO THIS SOFTWARE, INCLUDING WITHOUT LIMITATION ALL IMPLIED
  27.  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, AND IN
  28.  * NO EVENT SHALL THE UNIVERSITY OF WASHINGTON BE LIABLE FOR ANY SPECIAL,
  29.  * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
  30.  * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, TORT
  31.  * (INCLUDING NEGLIGENCE) OR STRICT LIABILITY, ARISING OUT OF OR IN CONNECTION
  32.  * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  33.  *
  34.  */
  35.  
  36.  
  37. #include <stdio.h>
  38. #include <ctype.h>
  39. #include <errno.h>
  40. #include <fcntl.h>
  41. #include "mail.h"
  42. #include "osdep.h"
  43. #include <time.h>
  44. #include <sys\stat.h>
  45. #include <dos.h>
  46. #include <io.h>
  47. #include "dawz.h"
  48. #include "rfc822.h"
  49. #include "misc.h"
  50.  
  51. /* Dawz mail routines */
  52.  
  53.  
  54. /* Driver dispatch used by MAIL */
  55.  
  56. DRIVER dawzdriver = {
  57.   "dawz",            /* driver name */
  58.   (DRIVER *) NIL,        /* next driver */
  59.   dawz_valid,            /* mailbox is valid for us */
  60.   dawz_parameters,        /* manipulate parameters */
  61.   dawz_find,            /* find mailboxes */
  62.   dawz_find_bboards,        /* find bboards */
  63.   dawz_find_all,        /* find all mailboxes */
  64.   dawz_find_bboards,        /* find all bboards */
  65.   dawz_subscribe,        /* subscribe to mailbox */
  66.   dawz_unsubscribe,        /* unsubscribe from mailbox */
  67.   dawz_subscribe_bboard,    /* subscribe to bboard */
  68.   dawz_subscribe_bboard,    /* unsubscribe (same as subscribe) */
  69.   dawz_create,            /* create mailbox */
  70.   dawz_delete,            /* delete mailbox */
  71.   dawz_rename,            /* rename mailbox */
  72.   dawz_open,            /* open mailbox */
  73.   dawz_close,            /* close mailbox */
  74.   dawz_fetchfast,        /* fetch message "fast" attributes */
  75.   dawz_fetchflags,        /* fetch message flags */
  76.   dawz_fetchstructure,        /* fetch message envelopes */
  77.   dawz_fetchheader,        /* fetch message header only */
  78.   dawz_fetchtext,        /* fetch message body only */
  79.   dawz_fetchbody,        /* fetch message body section */
  80.   dawz_setflag,            /* set message flag */
  81.   dawz_clearflag,        /* clear message flag */
  82.   dawz_search,            /* search for message based on criteria */
  83.   dawz_ping,            /* ping mailbox to see if still alive */
  84.   dawz_check,            /* check for new messages */
  85.   dawz_expunge,            /* expunge deleted messages */
  86.   dawz_copy,            /* copy messages to another mailbox */
  87.   dawz_move,            /* move messages to another mailbox */
  88.   dawz_append,            /* append string message to mailbox */
  89.   dawz_gc            /* garbage collect stream */
  90. };
  91.  
  92.                 /* prototype stream */
  93. MAILSTREAM dawzproto = {&dawzdriver};
  94.  
  95. /* Dawz mail validate mailbox
  96.  * Accepts: mailbox name
  97.  * Returns: our driver if name is valid, NIL otherwise
  98.  */
  99.  
  100. DRIVER *dawz_valid (char *name)
  101. {
  102.   return dawz_isvalid (name) ? &dawzdriver : (DRIVER *) NIL;
  103. }
  104.  
  105.  
  106. /* Dawz mail test for valid mailbox
  107.  * Accepts: mailbox name
  108.  * Returns: T if valid, NIL otherwise
  109.  */
  110.  
  111. long dawz_isvalid (char *name)
  112. {
  113.   int fd;
  114.   char *s,tmp[MAILTMPLEN];
  115.   struct stat sbuf;
  116.                 /* INBOX is always accepted */
  117.   if (!strcmp (ucase (strcpy (tmp,name)),"INBOX")) return T;
  118.                 /* if file, get its status */
  119.   if (*name != '{' && dawz_file (tmp,name) && (stat (tmp,&sbuf) == 0)) {
  120.                 /* allow empty file */
  121.     if (sbuf.st_size == 0) return T;
  122.     if ((fd = open (tmp,O_BINARY|O_RDONLY,NIL)) >= 0 && read (fd,tmp,64) >= 0){
  123.       close (fd);        /* close the file */
  124.       if ((s = strchr (tmp,'\015')) && s[1] == '\012') {
  125.     *s = '\0';        /* tie off header */
  126.                 /* must begin with dd-mmm-yy" */
  127.       if (((tmp[2] == '-' && tmp[6] == '-') ||
  128.            (tmp[1] == '-' && tmp[5] == '-')) &&
  129.           (s = strchr (tmp+20,',')) && strchr (s+2,';')) return LONGT;
  130.       }
  131.     }
  132.   }
  133.   return NIL;            /* failed miserably */
  134. }
  135.  
  136.  
  137. /* Dawz manipulate driver parameters
  138.  * Accepts: function code
  139.  *        function-dependent value
  140.  * Returns: function-dependent return value
  141.  */
  142.  
  143. void *dawz_parameters (long function,void *value)
  144. {
  145.   fatal ("Invalid dawz_parameters function");
  146. }
  147.  
  148. /* Dawz mail find list of mailboxes
  149.  * Accepts: mail stream
  150.  *        pattern to search
  151.  */
  152.  
  153. void dawz_find (MAILSTREAM *stream,char *pat)
  154. {
  155.   void *sdb = NIL;
  156.   struct find_t f;
  157.   char *s,tmp[MAILTMPLEN],file[MAILTMPLEN];
  158.   char *t = sm_read (&sdb);
  159.   int i = 0;
  160.   if (t) do if ((*t != '{') && strcmp (t,"INBOX") && pmatch (t,pat) &&
  161.         dawz_isvalid (t)) mm_mailbox (t);
  162.   while (t = sm_read (&sdb));    /* read subscription database */
  163.   else {            /* no subscriptions, do a directory */
  164.                 /* directory specified in pattern? */
  165.     if ((s = strrchr (pat,'\\')) || (*(s = pat + 1) == ':')) {
  166.       strncpy (file,pat,i = (++s) - pat);
  167.       file[i] = '\0';        /* tie off prefix */
  168.     }
  169.                 /* make fully-qualified file name */
  170.     if (!dawz_file (tmp,pat)) return;
  171.                 /* tie off directory name */
  172.     if (s = strrchr (tmp,'\\')) *s = '\0';
  173.     else tmp[2] = '\0';
  174.     sprintf (tmp + strlen (tmp),"\\*%s",DEFEXT);
  175.                 /* loop through matching files */
  176.     if (!_dos_findfirst (tmp,_A_NORMAL,&f)) do {
  177.                 /* suppress extension */
  178.       if (s = strchr (f.name,'.')) *s = '\0';
  179.       strcpy (file + i,f.name);    /* build file name */
  180.       if (pmatch (file,pat)) mm_mailbox (file);
  181.     } while (!_dos_findnext (&f));
  182.   }
  183. }
  184.  
  185.  
  186. /* Dawz mail find list of bboards
  187.  * Accepts: mail stream
  188.  *        pattern to search
  189.  */
  190.  
  191. void dawz_find_bboards (MAILSTREAM *stream,char *pat)
  192. {
  193.   /* Always a no-op */
  194. }
  195.  
  196. /* Dawz mail find list of all mailboxes
  197.  * Accepts: mail stream
  198.  *        pattern to search
  199.  */
  200.  
  201. void dawz_find_all (MAILSTREAM *stream,char *pat)
  202. {
  203.   struct find_t f;
  204.   char *s,tmp[MAILTMPLEN],file[MAILTMPLEN];
  205.   int i = 0;
  206.                 /* directory specified in pattern? */
  207.   if ((s = strrchr (pat,'\\')) || (*(s = pat + 1) == ':')) {
  208.     strncpy (file,pat,i = (++s) - pat);
  209.     file[i] = '\0';        /* tie off prefix */
  210.   }
  211.                 /* make fully-qualified file name */
  212.   if (!dawz_file (tmp,pat)) return;
  213.                 /* tie off directory name */
  214.   if (s = strrchr (tmp,'\\')) *s = '\0';
  215.   else tmp[2] = '\0';
  216.   sprintf (tmp + strlen (tmp),"\\*%s",DEFEXT);
  217.                 /* loop through matching files */
  218.   if (!_dos_findfirst (tmp,_A_NORMAL,&f)) do {
  219.                 /* suppress extension */
  220.     if (s = strchr (f.name,'.')) *s = '\0';
  221.     strcpy (file + i,f.name);    /* build file name */
  222.     if (pmatch (file,pat) && dawz_isvalid (file)) mm_mailbox (file);
  223.   } while (!_dos_findnext (&f));
  224. }
  225.  
  226. /* Dawz mail subscribe to mailbox
  227.  * Accepts: mail stream
  228.  *        mailbox to add to subscription list
  229.  * Returns: T on success, NIL on failure
  230.  */
  231.  
  232. long dawz_subscribe (MAILSTREAM *stream,char *mailbox)
  233. {
  234.   char tmp[MAILTMPLEN];
  235.   if (dawz_file (tmp,mailbox)) return sm_subscribe (mailbox);
  236.   else return dawz_badname (tmp,mailbox);
  237. }
  238.  
  239.  
  240. /* Dawz mail unsubscribe to mailbox
  241.  * Accepts: mail stream
  242.  *        mailbox to delete from subscription list
  243.  * Returns: T on success, NIL on failure
  244.  */
  245.  
  246. long dawz_unsubscribe (MAILSTREAM *stream,char *mailbox)
  247. {
  248.   char tmp[MAILTMPLEN];
  249.   if (dawz_file (tmp,mailbox)) return sm_unsubscribe (mailbox);
  250.   else return dawz_badname (tmp,mailbox);
  251. }
  252.  
  253.  
  254. /* Dawz mail subscribe to bboard
  255.  * Accepts: mail stream
  256.  *        bboard to add to subscription list
  257.  * Returns: T on success, NIL on failure
  258.  */
  259.  
  260. long dawz_subscribe_bboard (MAILSTREAM *stream,char *mailbox)
  261. {
  262.   return NIL;            /* never valid for Dawz */
  263. }
  264.  
  265. /* Dawz mail create mailbox
  266.  * Accepts: MAIL stream
  267.  *        mailbox name to create
  268.  * Returns: T on success, NIL on failure
  269.  */
  270.  
  271. long dawz_create (MAILSTREAM *stream,char *mailbox)
  272. {
  273.   char tmp[MAILTMPLEN];
  274.   int fd;
  275.   if (!dawz_file (tmp,mailbox)) return dawz_badname (tmp,mailbox);
  276.   if ((fd = open (tmp,O_WRONLY|O_CREAT|O_EXCL,S_IREAD|S_IWRITE)) < 0) {
  277.                 /* failed */
  278.     sprintf (tmp,"Can't create mailbox %s: %s",mailbox,strerror (errno));
  279.     mm_log (tmp,ERROR);
  280.     return NIL;
  281.   }
  282.   close (fd);            /* close the file */
  283.   return LONGT;            /* return success */
  284. }
  285.  
  286.  
  287. /* Dawz mail delete mailbox
  288.  * Accepts: MAIL stream
  289.  *        mailbox name to delete
  290.  * Returns: T on success, NIL on failure
  291.  */
  292.  
  293. long dawz_delete (MAILSTREAM *stream,char *mailbox)
  294. {
  295.   return dawz_rename (stream,mailbox,NIL);
  296. }
  297.  
  298.  
  299. /* Dawz mail rename mailbox
  300.  * Accepts: MAIL stream
  301.  *        old mailbox name
  302.  *        new mailbox name (or NIL for delete)
  303.  * Returns: T on success, NIL on failure
  304.  */
  305.  
  306. long dawz_rename (MAILSTREAM *stream,char *old,char *new)
  307. {
  308.   char tmp[MAILTMPLEN],file[MAILTMPLEN],lock[MAILTMPLEN],lockx[MAILTMPLEN];
  309.                 /* make file name */
  310.   if (!dawz_file (file,old)) return dawz_badname (tmp,old);
  311.   if (!dawz_file (tmp,new)) return dawz_badname (tmp,new);
  312.                 /* do the rename or delete operation */
  313.   if (new ? rename (file,tmp) : unlink (file)) {
  314.     sprintf (tmp,"Can't %s mailbox %s: %s",new ? "rename" : "delete",old,
  315.          strerror (errno));
  316.     mm_log (tmp,ERROR);
  317.     return NIL;
  318.   }
  319.   return LONGT;            /* return success */
  320. }
  321.  
  322. /* Dawz mail open
  323.  * Accepts: stream to open
  324.  * Returns: stream on success, NIL on failure
  325.  */
  326.  
  327. MAILSTREAM *dawz_open (MAILSTREAM *stream)
  328. {
  329.   long i;
  330.   int fd;
  331.   char *s;
  332.   char tmp[MAILTMPLEN];
  333.   struct stat sbuf;
  334.                 /* return prototype for OP_PROTOTYPE call */
  335.   if (!stream) return &dawzproto;
  336.   if (LOCAL) {            /* close old file if stream being recycled */
  337.     dawz_close (stream);    /* dump and save the changes */
  338.     stream->dtb = &dawzdriver;    /* reattach this driver */
  339.     mail_free_cache (stream);    /* clean up cache */
  340.   }
  341.   else {            /* flush flagstring and flags if any */
  342.     if (stream->flagstring) fs_give ((void **) &stream->flagstring);
  343.     for (i = 0; i < NUSERFLAGS; ++i) stream->user_flags[i] = NIL;
  344.   }
  345.   if (!dawz_file (tmp,stream->mailbox))
  346.     return (MAILSTREAM *) dawz_badname (tmp,stream->mailbox);
  347.   if (((fd = open (tmp,O_BINARY|(stream->readonly ? O_RDONLY:O_RDWR),NIL)) < 0)
  348.       && (strcmp (ucase (stream->mailbox),"INBOX") ||
  349.       ((fd = open (tmp,O_BINARY|O_RDWR|O_CREAT|O_EXCL,S_IREAD|S_IWRITE))
  350.        < 0))) {        /* open, possibly creating INBOX */
  351.     sprintf (tmp,"Can't open mailbox: %s",strerror (errno));
  352.     mm_log (tmp,ERROR);
  353.     return NIL;
  354.   }
  355.                 /* hokey default for local host */
  356.   if (!lhostn) lhostn = cpystr ("localhost");
  357.   stream->local = fs_get (sizeof (DAWZLOCAL));
  358.                 /* canonicalize the stream mailbox name */
  359.   fs_give ((void **) &stream->mailbox);
  360.   if (s = strchr ((s = strrchr (tmp,'\\')) ? s : tmp,'.')) *s = '\0';
  361.   stream->mailbox = cpystr (tmp);
  362.   LOCAL->fd = fd;        /* note the file */
  363.   LOCAL->filesize = 0;        /* initialize parsed file size */
  364.   stream->sequence++;        /* bump sequence number */
  365.                 /* parse mailbox */
  366.   stream->nmsgs = stream->recent = 0;
  367.   if (dawz_ping (stream) && !stream->nmsgs)
  368.     mm_log ("Mailbox is empty",(long) NIL);
  369.   return stream;        /* return stream to caller */
  370. }
  371.  
  372. /* Dawz mail close
  373.  * Accepts: MAIL stream
  374.  */
  375.  
  376. void dawz_close (MAILSTREAM *stream)
  377. {
  378.   long i;
  379.   if (stream && LOCAL) {    /* only if a file is open */
  380.     close (LOCAL->fd);        /* close the local file */
  381.                 /* nuke the local data */
  382.     fs_give ((void **) &stream->local);
  383.     stream->dtb = NIL;        /* log out the DTB */
  384.   }
  385. }
  386.  
  387.  
  388. /* Dawz mail fetch fast information
  389.  * Accepts: MAIL stream
  390.  *        sequence
  391.  */
  392.  
  393. void dawz_fetchfast (MAILSTREAM *stream,char *sequence)
  394. {
  395.   return;            /* no-op for local mail */
  396. }
  397.  
  398.  
  399. /* Dawz mail fetch flags
  400.  * Accepts: MAIL stream
  401.  *        sequence
  402.  */
  403.  
  404. void dawz_fetchflags (MAILSTREAM *stream,char *sequence)
  405. {
  406.   return;            /* no-op for local mail */
  407. }
  408.  
  409. /* Dawz string driver for file stringstructs */
  410.  
  411. STRINGDRIVER dawz_string = {
  412.   dawz_string_init,        /* initialize string structure */
  413.   dawz_string_next,        /* get next byte in string structure */
  414.   dawz_string_setpos        /* set position in string structure */
  415. };
  416.  
  417.  
  418. /* Cache buffer for file stringstructs */
  419.  
  420. #define DOSCHUNKLEN 4096
  421. char dos_chunk[DOSCHUNKLEN];
  422.  
  423.  
  424. /* Initialize dawz string structure for file stringstruct
  425.  * Accepts: string structure
  426.  *        pointer to string
  427.  *        size of string
  428.  */
  429.  
  430. void dawz_string_init (STRING *s,void *data,unsigned long size)
  431. {
  432.   DAWZDATA *d = (DAWZDATA *) data;
  433.   s->data = (void *) d->fd;    /* note fd */
  434.   s->data1 = d->pos;        /* note file offset */
  435.   s->size = size;        /* note size */
  436.   s->curpos = s->chunk = dos_chunk;
  437.   s->chunksize = (unsigned long) DOSCHUNKLEN;
  438.   s->offset = 0;        /* initial position */
  439.                 /* and size of data */
  440.   s->cursize = min ((long) DOSCHUNKLEN,size);
  441.                 /* move to that position in the file */
  442.   lseek (d->fd,d->pos,SEEK_SET);
  443.   read (d->fd,s->chunk,(unsigned int) s->cursize);
  444. }
  445.  
  446. /* Get next character from file stringstruct
  447.  * Accepts: string structure
  448.  * Returns: character, string structure chunk refreshed
  449.  */
  450.  
  451. char dawz_string_next (STRING *s)
  452. {
  453.   char c = *s->curpos++;    /* get next byte */
  454.                 /* move to next chunk */
  455.   SETPOS (s,s->offset + s->chunksize);
  456.   return c;            /* return the byte */
  457. }
  458.  
  459.  
  460. /* Set string pointer position for file stringstruct
  461.  * Accepts: string structure
  462.  *        new position
  463.  */
  464.  
  465. void dawz_string_setpos (STRING *s,unsigned long i)
  466. {
  467.   s->offset = i;        /* set new offset */
  468.   s->curpos = s->chunk;        /* reset position */
  469.                 /* set size of data */
  470.   if (s->cursize = s->size > s->offset ? min ((long) DOSCHUNKLEN,SIZE (s)):0) {
  471.                 /* move to that position in the file */
  472.     lseek ((int) s->data,s->data1 + s->offset,SEEK_SET);
  473.     read ((int) s->data,s->curpos,(unsigned int) s->cursize);
  474.   }
  475. }
  476.  
  477. /* Dawz mail fetch structure
  478.  * Accepts: MAIL stream
  479.  *        message # to fetch
  480.  *        pointer to return body
  481.  * Returns: envelope of this message, body returned in body value
  482.  *
  483.  * Fetches the "fast" information as well
  484.  */
  485.  
  486. #define MAXHDR (unsigned long) 4*MAILTMPLEN
  487.  
  488. ENVELOPE *dawz_fetchstructure (MAILSTREAM *stream,long msgno,BODY **body)
  489. {
  490.   LONGCACHE *lelt;
  491.   ENVELOPE **env;
  492.   BODY **b;
  493.   STRING bs;
  494.   DAWZDATA d;
  495.   unsigned long hdrsize;
  496.   unsigned long hdrpos = dawz_header (stream,msgno,&hdrsize);
  497.   unsigned long textsize = mail_elt (stream,msgno)->rfc822_size - hdrsize;
  498.                 /* limit header size */
  499.   if (hdrsize > MAXHDR) hdrsize = MAXHDR;
  500.   if (stream->scache) {        /* short cache */
  501.     if (msgno != stream->msgno){/* flush old poop if a different message */
  502.       mail_free_envelope (&stream->env);
  503.       mail_free_body (&stream->body);
  504.     }
  505.     stream->msgno = msgno;
  506.     env = &stream->env;        /* get pointers to envelope and body */
  507.     b = &stream->body;
  508.   }
  509.   else {            /* long cache */
  510.     lelt = mail_lelt (stream,msgno);
  511.     env = &lelt->env;        /* get pointers to envelope and body */
  512.     b = &lelt->body;
  513.   }
  514.  
  515.   if ((body && !*b) || !*env) {    /* have the poop we need? */
  516.     char *hdr = (char *) fs_get (hdrsize + 1);
  517.     char *tmp = (char *) fs_get (MAXHDR);
  518.     mail_free_envelope (env);    /* flush old envelope and body */
  519.     mail_free_body (b);
  520.                 /* get to header position */
  521.     lseek (LOCAL->fd,hdrpos,SEEK_SET);
  522.                 /* read the text */
  523.     if (read (LOCAL->fd,hdr,(unsigned int) hdrsize) >= 0) {
  524.       if (hdr[hdrsize-1] != '\012') hdr[hdrsize-1] = '\012';
  525.       hdr[hdrsize] = '\0';    /* make sure tied off */
  526.       d.fd = LOCAL->fd;        /* set initial stringstruct */
  527.       d.pos = hdrpos + hdrsize;
  528.       INIT (&bs,dawz_string,(void *) &d,textsize);
  529.                 /* parse envelope and body */
  530.       rfc822_parse_msg (env,body ? b : NIL,hdr,hdrsize,&bs,lhostn,tmp);
  531.     }
  532.     fs_give ((void **) &tmp);
  533.     fs_give ((void **) &hdr);
  534.   }
  535.   if (body) *body = *b;        /* return the body */
  536.   return *env;            /* return the envelope */
  537. }
  538.  
  539. /* Dawz mail fetch message header
  540.  * Accepts: MAIL stream
  541.  *        message # to fetch
  542.  * Returns: message header in RFC822 format
  543.  */
  544.  
  545. char *dawz_fetchheader (MAILSTREAM *stream,long msgno)
  546. {
  547.   unsigned long hdrsize;
  548.   unsigned long hdrpos = dawz_header (stream,msgno,&hdrsize);
  549.                 /* set default gets routine */
  550.   if (!mailgets) mailgets = mm_gets;
  551.   if (stream->text) fs_give ((void **) &stream->text);
  552.                 /* get to header position */
  553.   lseek (LOCAL->fd,hdrpos,SEEK_SET);
  554.   return stream->text = (*mailgets) (dawz_read,stream,hdrsize);
  555. }
  556.  
  557.  
  558. /* Dawz mail fetch message text (body only)
  559.  * Accepts: MAIL stream
  560.  *        message # to fetch
  561.  * Returns: message text in RFC822 format
  562.  */
  563.  
  564. char *dawz_fetchtext (MAILSTREAM *stream,long msgno)
  565. {
  566.   unsigned long hdrsize;
  567.   unsigned long hdrpos = dawz_header (stream,msgno,&hdrsize);
  568.   unsigned long textsize = mail_elt (stream,msgno)->rfc822_size - hdrsize;
  569.                 /* set default gets routine */
  570.   if (!mailgets) mailgets = mm_gets;
  571.   if (stream->text) fs_give ((void **) &stream->text);
  572.                 /* mark message as seen */
  573.   mail_elt (stream,msgno)->seen = T;
  574.                 /* recalculate status */
  575.   dawz_update_status (stream,msgno);
  576.                 /* get to text position */
  577.   lseek (LOCAL->fd,hdrpos + hdrsize,SEEK_SET);
  578.   return stream->text = (*mailgets) (dawz_read,stream,textsize);
  579. }
  580.  
  581. /* Dawz fetch message body as a structure
  582.  * Accepts: Mail stream
  583.  *        message # to fetch
  584.  *        section specifier
  585.  *        pointer to length
  586.  * Returns: pointer to section of message body
  587.  */
  588.  
  589. char *dawz_fetchbody (MAILSTREAM *stream,long m,char *s,unsigned long *len)
  590. {
  591.   BODY *b;
  592.   PART *pt;
  593.   unsigned long i;
  594.   unsigned long base;
  595.   unsigned long offset = 0;
  596.   unsigned long hdrpos = dawz_header (stream,m,&base);
  597.   MESSAGECACHE *elt = mail_elt (stream,m);
  598.                 /* set default gets routine */
  599.   if (!mailgets) mailgets = mm_gets;
  600.   if (stream->text) fs_give ((void **) &stream->text);
  601.                 /* make sure have a body */
  602.   if (!(dawz_fetchstructure (stream,m,&b) && b && s && *s &&
  603.     ((i = strtol (s,&s,10)) > 0))) return NIL;
  604.   do {                /* until find desired body part */
  605.                 /* multipart content? */
  606.     if (b->type == TYPEMULTIPART) {
  607.       pt = b->contents.part;    /* yes, find desired part */
  608.       while (--i && (pt = pt->next));
  609.       if (!pt) return NIL;    /* bad specifier */
  610.                 /* note new body, check valid nesting */
  611.       if (((b = &pt->body)->type == TYPEMULTIPART) && !*s) return NIL;
  612.       offset = pt->offset;    /* get new offset */
  613.     }
  614.     else if (i != 1) return NIL;/* otherwise must be section 1 */
  615.                 /* need to go down further? */
  616.     if (i = *s) switch (b->type) {
  617.     case TYPEMESSAGE:        /* embedded message, calculate new base */
  618.       offset = b->contents.msg.offset;
  619.       b = b->contents.msg.body;    /* get its body, drop into multipart case */
  620.     case TYPEMULTIPART:        /* multipart, get next section */
  621.       if ((*s++ == '.') && (i = strtol (s,&s,10)) > 0) break;
  622.     default:            /* bogus subpart specification */
  623.       return NIL;
  624.     }
  625.   } while (i);
  626.                 /* lose if body bogus */
  627.   if ((!b) || b->type == TYPEMULTIPART) return NIL;
  628.   elt->seen = T;        /* mark message as seen */
  629.   dawz_update_status (stream,m);/* recalculate status */
  630.   lseek (LOCAL->fd,hdrpos + base + offset,SEEK_SET);
  631.   return stream->text = (*mailgets) (dawz_read,stream,*len = b->size.bytes);
  632. }
  633.  
  634. /* Dawz mail read
  635.  * Accepts: MAIL stream
  636.  *        number of bytes to read
  637.  *        buffer address
  638.  * Returns: T if success, NIL otherwise
  639.  */
  640.  
  641. long dawz_read (MAILSTREAM *stream,unsigned long count,char *buffer)
  642. {
  643.   return read (LOCAL->fd,buffer,(unsigned int) count) ? T : NIL;
  644. }
  645.  
  646. /* Dawz locate header for a message
  647.  * Accepts: MAIL stream
  648.  *        message number
  649.  *        pointer to returned header size
  650.  * Returns: position of header in file
  651.  */
  652.  
  653. unsigned long dawz_header (MAILSTREAM *stream,long msgno,unsigned long *size)
  654. {
  655.   long siz;
  656.   long i = 0;
  657.   int q = 0;
  658.   char *s;
  659.   char tmp[MAILTMPLEN];
  660.   MESSAGECACHE *elt = mail_elt (stream,msgno);
  661.   long pos = elt->data1 + (elt->data2 >> 24);
  662.                 /* is size known? */
  663.   if (!(*size = (elt->data2 & (unsigned long) 0xffffff))) {
  664.                 /* get to header position */
  665.     lseek (LOCAL->fd,pos,SEEK_SET);
  666.                 /* search message for CRLF CRLF */
  667.     for (siz = 0; siz < elt->rfc822_size; siz++) {
  668.                 /* read another buffer as necessary */
  669.       if (--i <= 0)        /* buffer empty? */
  670.     if (read (LOCAL->fd,s = tmp,
  671.            i = min (elt->rfc822_size - siz,(long) MAILTMPLEN)) < 0)
  672.       return pos;        /* I/O error? */
  673.       switch (q) {        /* sniff at buffer */
  674.       case 0:            /* first character */
  675.     q = (*s++ == '\015') ? 1 : 0;
  676.     break;
  677.       case 1:            /* second character */
  678.     q = (*s++ == '\012') ? 2 : 0;
  679.     break;
  680.       case 2:            /* third character */
  681.     q = (*s++ == '\015') ? 3 : 0;
  682.     break;
  683.       case 3:            /* fourth character */
  684.     if (*s++ == '\012') {    /* have the sequence? */
  685.                 /* yes, note for later */
  686.       elt->data2 |= (*size = siz);
  687.       return pos;        /* return to caller */
  688.     }
  689.     q = 0;            /* lost... */
  690.     break;
  691.       }
  692.     }
  693.   }
  694.   return pos;            /* have position */
  695. }
  696.  
  697. /* Dawz mail set flag
  698.  * Accepts: MAIL stream
  699.  *        sequence
  700.  *        flag(s)
  701.  */
  702.  
  703. void dawz_setflag (MAILSTREAM *stream,char *sequence,char *flag)
  704. {
  705.   MESSAGECACHE *elt;
  706.   long i;
  707.   short f = dawz_getflags (stream,flag);
  708.   if (!f) return;        /* no-op if no flags to modify */
  709.                 /* get sequence and loop on it */
  710.   if (mail_sequence (stream,sequence)) for (i = 1; i <= stream->nmsgs; i++)
  711.     if ((elt = mail_elt (stream,i))->sequence) {
  712.                 /* set all requested flags */
  713.       if (f&fSEEN) elt->seen = T;
  714.       if (f&fDELETED) elt->deleted = T;
  715.       if (f&fFLAGGED) elt->flagged = T;
  716.       if (f&fANSWERED) elt->answered = T;
  717.                 /* recalculate status */
  718.       dawz_update_status (stream,i);
  719.     }
  720. }
  721.  
  722. /* Dawz mail clear flag
  723.  * Accepts: MAIL stream
  724.  *        sequence
  725.  *        flag(s)
  726.  */
  727.  
  728. void dawz_clearflag (MAILSTREAM *stream,char *sequence,char *flag)
  729. {
  730.   MESSAGECACHE *elt;
  731.   long i;
  732.   short f = dawz_getflags (stream,flag);
  733.   if (!f) return;        /* no-op if no flags to modify */
  734.                 /* get sequence and loop on it */
  735.   if (mail_sequence (stream,sequence)) for (i = 1; i <= stream->nmsgs; i++)
  736.     if ((elt = mail_elt (stream,i))->sequence) {
  737.                 /* clear all requested flags */
  738.       if (f&fSEEN) elt->seen = NIL;
  739.       if (f&fDELETED) elt->deleted = NIL;
  740.       if (f&fFLAGGED) elt->flagged = NIL;
  741.       if (f&fANSWERED) elt->answered = NIL;
  742.                 /* recalculate status */
  743.       dawz_update_status (stream,i);
  744.     }
  745. }
  746.  
  747. /* Dawz mail search for messages
  748.  * Accepts: MAIL stream
  749.  *        search criteria
  750.  */
  751.  
  752. void dawz_search (MAILSTREAM *stream,char *criteria)
  753. {
  754.   long i,n;
  755.   char *d;
  756.   search_t f;
  757.                 /* initially all searched */
  758.   for (i = 1; i <= stream->nmsgs; ++i) mail_elt (stream,i)->searched = T;
  759.                 /* get first criterion */
  760.   if (criteria && (criteria = strtok (criteria," "))) {
  761.                 /* for each criterion */
  762.     for (; criteria; (criteria = strtok (NIL," "))) {
  763.       f = NIL; d = NIL; n = 0;    /* init then scan the criterion */
  764.       switch (*ucase (criteria)) {
  765.       case 'A':            /* possible ALL, ANSWERED */
  766.     if (!strcmp (criteria+1,"LL")) f = dawz_search_all;
  767.     else if (!strcmp (criteria+1,"NSWERED")) f = dawz_search_answered;
  768.     break;
  769.       case 'B':            /* possible BCC, BEFORE, BODY */
  770.     if (!strcmp (criteria+1,"CC"))
  771.       f = dawz_search_string (dawz_search_bcc,&d,&n);
  772.     else if (!strcmp (criteria+1,"EFORE"))
  773.       f = dawz_search_date (dawz_search_before,&n);
  774.     else if (!strcmp (criteria+1,"ODY"))
  775.       f = dawz_search_string (dawz_search_body,&d,&n);
  776.     break;
  777.       case 'C':            /* possible CC */
  778.     if (!strcmp (criteria+1,"C")) 
  779.       f = dawz_search_string (dawz_search_cc,&d,&n);
  780.     break;
  781.       case 'D':            /* possible DELETED */
  782.     if (!strcmp (criteria+1,"ELETED")) f = dawz_search_deleted;
  783.     break;
  784.       case 'F':            /* possible FLAGGED, FROM */
  785.     if (!strcmp (criteria+1,"LAGGED")) f = dawz_search_flagged;
  786.     else if (!strcmp (criteria+1,"ROM"))
  787.       f = dawz_search_string (dawz_search_from,&d,&n);
  788.     break;
  789.       case 'K':            /* possible KEYWORD */
  790.     if (!strcmp (criteria+1,"EYWORD"))
  791.       f = dawz_search_flag (dawz_search_keyword,&n,stream);
  792.     break;
  793.       case 'N':            /* possible NEW */
  794.     if (!strcmp (criteria+1,"EW")) f = dawz_search_new;
  795.     break;
  796.  
  797.       case 'O':            /* possible OLD, ON */
  798.     if (!strcmp (criteria+1,"LD")) f = dawz_search_old;
  799.     else if (!strcmp (criteria+1,"N"))
  800.       f = dawz_search_date (dawz_search_on,&n);
  801.     break;
  802.       case 'R':            /* possible RECENT */
  803.     if (!strcmp (criteria+1,"ECENT")) f = dawz_search_recent;
  804.     break;
  805.       case 'S':            /* possible SEEN, SINCE, SUBJECT */
  806.     if (!strcmp (criteria+1,"EEN")) f = dawz_search_seen;
  807.     else if (!strcmp (criteria+1,"INCE"))
  808.       f = dawz_search_date (dawz_search_since,&n);
  809.     else if (!strcmp (criteria+1,"UBJECT"))
  810.       f = dawz_search_string (dawz_search_subject,&d,&n);
  811.     break;
  812.       case 'T':            /* possible TEXT, TO */
  813.     if (!strcmp (criteria+1,"EXT"))
  814.       f = dawz_search_string (dawz_search_text,&d,&n);
  815.     else if (!strcmp (criteria+1,"O"))
  816.       f = dawz_search_string (dawz_search_to,&d,&n);
  817.     break;
  818.       case 'U':            /* possible UN* */
  819.     if (criteria[1] == 'N') {
  820.       if (!strcmp (criteria+2,"ANSWERED")) f = dawz_search_unanswered;
  821.       else if (!strcmp (criteria+2,"DELETED")) f = dawz_search_undeleted;
  822.       else if (!strcmp (criteria+2,"FLAGGED")) f = dawz_search_unflagged;
  823.       else if (!strcmp (criteria+2,"KEYWORD"))
  824.         f = dawz_search_flag (dawz_search_unkeyword,&n,stream);
  825.       else if (!strcmp (criteria+2,"SEEN")) f = dawz_search_unseen;
  826.     }
  827.     break;
  828.       default:            /* we will barf below */
  829.     break;
  830.       }
  831.       if (!f) {            /* if can't determine any criteria */
  832.     mm_log ("Unknown search criterion",ERROR);
  833.     return;
  834.       }
  835.                 /* run the search criterion */
  836.       for (i = 1; i <= stream->nmsgs; ++i)
  837.     if (mail_elt (stream,i)->searched && !(*f) (stream,i,d,n))
  838.       mail_elt (stream,i)->searched = NIL;
  839.     }
  840.                 /* report search results to main program */
  841.     for (i = 1; i <= stream->nmsgs; ++i)
  842.       if (mail_elt (stream,i)->searched) mail_searched (stream,i);
  843.   }
  844. }
  845.  
  846. /* Dawz mail ping mailbox
  847.  * Accepts: MAIL stream
  848.  * Returns: T if stream still alive, NIL if not
  849.  */
  850.  
  851. long dawz_ping (MAILSTREAM *stream)
  852. {
  853.   long i = 0;
  854.   long r,j;
  855.   struct stat sbuf;
  856.                 /* punt if stream no longer alive */
  857.   if (!(stream && LOCAL)) return NIL;
  858.                 /* parse mailbox, punt if parse dies */
  859.   return (dawz_parse (stream)) ? T : NIL;
  860. }
  861.  
  862.  
  863. /* Dawz mail check mailbox (reparses status too)
  864.  * Accepts: MAIL stream
  865.  */
  866.  
  867. void dawz_check (MAILSTREAM *stream)
  868. {
  869.   long i = 1;
  870.   if (dawz_ping (stream)) {    /* ping mailbox */
  871.                 /* get new message status */
  872.     while (i <= stream->nmsgs) mail_elt (stream,i++);
  873.     mm_log ("Check completed",(long) NIL);
  874.   }
  875. }
  876.  
  877. /* Dawz mail expunge mailbox
  878.  * Accepts: MAIL stream
  879.  */
  880.  
  881. void dawz_expunge (MAILSTREAM *stream)
  882. {
  883.   unsigned long i = 1;
  884.   unsigned long j,k,m,recent;
  885.   unsigned long n = 0;
  886.   unsigned long delta = 0;
  887.   MESSAGECACHE *elt;
  888.   char tmp[MAILTMPLEN];
  889.                 /* do nothing if stream dead */
  890.   if (!dawz_ping (stream)) return;
  891.   if (stream->readonly) {    /* won't do on readonly files! */
  892.     mm_log ("Expunge ignored on readonly mailbox",WARN);
  893.     return;
  894.   }
  895.   mm_critical (stream);        /* go critical */
  896.   recent = stream->recent;    /* get recent now that pinged */ 
  897.   while (i <= stream->nmsgs) {    /* for each message */
  898.                 /* if deleted */
  899.     if ((elt = mail_elt (stream,i))->deleted) {
  900.       if (elt->recent) --recent;/* if recent, note one less recent message */
  901.                 /* number of bytes to delete */
  902.       delta += (elt->data2 >> 24) + elt->rfc822_size;
  903.       mail_expunged (stream,i);    /* notify upper levels */
  904.       n++;            /* count up one more deleted message */
  905.     }
  906.     else if (i++ && delta) {    /* preserved message */
  907.       j = elt->data1;        /* j is byte to copy, k is number of bytes */
  908.       k = (elt->data2 >> 24) + elt->rfc822_size;
  909.       do {            /* read from source position */
  910.     m = min (k,(unsigned long) MAILTMPLEN);
  911.     lseek (LOCAL->fd,j,SEEK_SET);
  912.     read (LOCAL->fd,tmp,(unsigned int) m);
  913.                 /* write to destination position */
  914.     lseek (LOCAL->fd,j - delta,SEEK_SET);
  915.     write (LOCAL->fd,tmp,(unsigned int) m);
  916.     j += m;            /* next chunk, perhaps */
  917.       } while (k -= m);        /* until done */
  918.       elt->data1 -= delta;    /* note the new address of this text */
  919.     }
  920.   }
  921.   if (n) {            /* truncate file after last message */
  922.     chsize (LOCAL->fd,LOCAL->filesize -= delta);
  923.     sprintf (tmp,"Expunged %ld messages",n);
  924.     mm_log (tmp,(long) NIL);    /* output the news */
  925.   }
  926.   else mm_log ("No messages deleted, so no update needed",(long) NIL);
  927.   mm_nocritical (stream);    /* release critical */
  928.                 /* notify upper level of new mailbox size */
  929.   mail_exists (stream,stream->nmsgs);
  930.   mail_recent (stream,recent);
  931. }
  932.  
  933. /* Dawz mail copy message(s)
  934.  * Accepts: MAIL stream
  935.  *        sequence
  936.  *        destination mailbox
  937.  * Returns: T if success, NIL if failed
  938.  */
  939.  
  940. long dawz_copy (MAILSTREAM *stream,char *sequence,char *mailbox)
  941. {
  942.                 /* copy the messages */
  943.   return (mail_sequence (stream,sequence)) ?
  944.     dawz_copy_messages (stream,mailbox) : NIL;
  945. }
  946.  
  947.  
  948. /* Dawz mail move message(s)
  949.  * Accepts: MAIL stream
  950.  *        sequence
  951.  *        destination mailbox
  952.  * Returns: T if success, NIL if failed
  953.  */
  954.  
  955. long dawz_move (MAILSTREAM *stream,char *sequence,char *mailbox)
  956. {
  957.   long i;
  958.   MESSAGECACHE *elt;
  959.   if (!(mail_sequence (stream,sequence) &&
  960.     dawz_copy_messages (stream,mailbox))) return NIL;
  961.                 /* delete all requested messages */
  962.   for (i = 1; i <= stream->nmsgs; i++)
  963.     if ((elt = mail_elt (stream,i))->sequence) {
  964.       elt->deleted = T;        /* mark message deleted */
  965.                 /* recalculate status */
  966.       dawz_update_status (stream,i);
  967.     }
  968.   return T;
  969. }
  970.  
  971. /* Dawz mail append message from stringstruct
  972.  * Accepts: MAIL stream
  973.  *        destination mailbox
  974.  *        stringstruct of messages to append
  975.  * Returns: T if append successful, else NIL
  976.  */
  977.  
  978. long dawz_append (MAILSTREAM *stream,char *mailbox,STRING *message)
  979. {
  980.   int fd;
  981.   struct stat sbuf;
  982.   char tmp[MAILTMPLEN];
  983.   time_t ti;
  984.   struct tm *t;
  985.   long i;
  986.   long size = SIZE (message);
  987.   tzset ();            /* initialize timezone stuff */
  988.                 /* make sure valid mailbox */
  989.   if (!dawz_isvalid (mailbox)) {
  990.     sprintf (tmp,"Not a Dawz-format mailbox: %s",mailbox);
  991.     mm_log (tmp,ERROR);
  992.     return NIL;
  993.   }
  994.   if (!dawz_file (tmp,mailbox)) return dawz_badname (tmp,mailbox);
  995.                 /* open the destination */
  996.   if ((fd = open (tmp,O_BINARY|O_WRONLY|O_APPEND|O_CREAT,S_IREAD|S_IWRITE))<0){
  997.     sprintf (tmp,"Can't open append mailbox: %s",strerror (errno));
  998.     mm_log (tmp,ERROR);
  999.     return NIL;
  1000.   }
  1001.   mm_critical (stream);        /* go critical */
  1002.   fstat (fd,&sbuf);        /* get current file size */
  1003.   ti = time (0);        /* get time now */
  1004.   t = localtime (&ti);        /* output local time */
  1005.   sprintf (tmp,"%2d-%s-%d %02d:%02d:%02d-%s,%ld;000000000000\015\012",
  1006.        t->tm_mday,months[t->tm_mon],t->tm_year+1900,
  1007.        t->tm_hour,t->tm_min,t->tm_sec,tzname[t->tm_isdst],size);
  1008.  
  1009.                 /* write header */
  1010.   if (write (fd,tmp,strlen (tmp)) < 0) {
  1011.     sprintf (tmp,"Header write failed: %s",strerror (errno));
  1012.     mm_log (tmp,ERROR);
  1013.     chsize (fd,sbuf.st_size);
  1014.   }  
  1015.   else while (size) {        /* while there is more data to write */
  1016.     if (write (fd,message->curpos,i = min (size,message->cursize)) < 0) {
  1017.       sprintf (tmp,"Message append failed: %s",strerror (errno));
  1018.       mm_log (tmp,ERROR);
  1019.       chsize (fd,sbuf.st_size);
  1020.       break;
  1021.     }
  1022.     size -= i;            /* note that we wrote out this much */
  1023.     message->curpos += i;
  1024.     message->cursize -= i;
  1025.     (message->dtb->next) (message);
  1026.   }
  1027.   close (fd);            /* close the file */
  1028.   mm_nocritical (stream);    /* release critical */
  1029.   return T;            /* return success */
  1030. }
  1031.  
  1032.  
  1033. /* Dawz garbage collect stream
  1034.  * Accepts: Mail stream
  1035.  *        garbage collection flags
  1036.  */
  1037.  
  1038. void dawz_gc (MAILSTREAM *stream,long gcflags)
  1039. {
  1040.   /* nothing here for now */
  1041. }
  1042.  
  1043. /* Internal routines */
  1044.  
  1045.  
  1046. /* Dawz mail generate file string
  1047.  * Accepts: temporary buffer to write into
  1048.  *        mailbox name string
  1049.  * Returns: local file string
  1050.  */
  1051.  
  1052. char *dawz_file (char *dst,char *name)
  1053. {
  1054.   char *s;
  1055.                 /* forbid extensions of filename */
  1056.   if (strchr ((s = strrchr (name,'\\')) ? s : name,'.')) return NIL;
  1057.                 /* absolute path name? */
  1058.   if ((*name == '\\') || (name[1] == ':')) sprintf (dst,"%s%s",name,DEFEXT);
  1059.   else sprintf (dst,"%s\\%s%s",myhomedir (),name,DEFEXT);
  1060.   return ucase (dst);
  1061. }
  1062.  
  1063.  
  1064. /* Return bad file name error message
  1065.  * Accepts: temporary buffer
  1066.  *        file name
  1067.  * Returns: long NIL always
  1068.  */
  1069.  
  1070. long dawz_badname (char *tmp,char *s)
  1071. {
  1072.   sprintf (tmp,"Invalid mailbox name: %s",s);
  1073.   mm_log (tmp,ERROR);
  1074.   return (long) NIL;
  1075. }
  1076.  
  1077. /* Parse flag list
  1078.  * Accepts: MAIL stream
  1079.  *        flag list as a character string
  1080.  * Returns: system flags
  1081.  */
  1082.  
  1083. long dawz_getflags (MAILSTREAM *stream,char *flag)
  1084. {
  1085.   char tmp[MAILTMPLEN];
  1086.   char key[MAILTMPLEN];
  1087.   char *t,*s;
  1088.   short f = 0;
  1089.   long i;
  1090.   short j;
  1091.   if (flag && *flag) {        /* no-op if no flag string */
  1092.                 /* check if a list and make sure valid */
  1093.     if ((i = (*flag == '(')) ^ (flag[strlen (flag)-1] == ')')) {
  1094.       mm_log ("Bad flag list",ERROR);
  1095.       return NIL;
  1096.     }
  1097.                 /* copy the flag string w/o list construct */
  1098.     strncpy (tmp,flag+i,(j = strlen (flag) - (2*i)));
  1099.     tmp[j] = '\0';        /* tie off tail */
  1100.  
  1101.                 /* make uppercase, find first, parse */
  1102.     if (t = strtok (ucase (tmp)," ")) do {
  1103.       i = 0;            /* no flag yet */
  1104.                 /* system flag, dispatch on first character */
  1105.       if (*t == '\\') switch (*++t) {
  1106.       case 'S':            /* possible \Seen flag */
  1107.     if (t[1] == 'E' && t[2] == 'E' && t[3] == 'N' && t[4] == '\0')
  1108.       f |= i = fSEEN;
  1109.     break;
  1110.       case 'D':            /* possible \Deleted flag */
  1111.     if (t[1] == 'E' && t[2] == 'L' && t[3] == 'E' && t[4] == 'T' &&
  1112.         t[5] == 'E' && t[6] == 'D' && t[7] == '\0') f |= i = fDELETED;
  1113.     break;
  1114.       case 'F':            /* possible \Flagged flag */
  1115.     if (t[1] == 'L' && t[2] == 'A' && t[3] == 'G' && t[4] == 'G' &&
  1116.         t[5] == 'E' && t[6] == 'D' && t[7] == '\0') f |= i = fFLAGGED;
  1117.     break;
  1118.       case 'A':            /* possible \Answered flag */
  1119.     if (t[1] == 'N' && t[2] == 'S' && t[3] == 'W' && t[4] == 'E' &&
  1120.         t[5] == 'R' && t[6] == 'E' && t[7] == 'D' && t[8] == '\0')
  1121.       f |= i = fANSWERED;
  1122.     break;
  1123.       default:            /* unknown */
  1124.     break;
  1125.       }
  1126.       if (!i) {            /* didn't find a matching flag? */
  1127.     sprintf (key,"Unknown flag: %.80s",t);
  1128.     mm_log (key,ERROR);
  1129.     return NIL;        /* return no system flags */
  1130.       }
  1131.                 /* parse next flag */
  1132.     } while (t = strtok (NIL," "));
  1133.   }
  1134.   return f;
  1135. }
  1136.  
  1137. /* Dawz mail parse mailbox
  1138.  * Accepts: MAIL stream
  1139.  * Returns: T if parse OK
  1140.  *        NIL if failure, stream aborted
  1141.  */
  1142.  
  1143. long dawz_parse (MAILSTREAM *stream)
  1144. {
  1145.   struct stat sbuf;
  1146.   MESSAGECACHE *elt = NIL;
  1147.   char c,*s,*t;
  1148.   char tmp[MAILTMPLEN];
  1149.   long i;
  1150.   long curpos = LOCAL->filesize;
  1151.   long nmsgs = stream->nmsgs;
  1152.   long recent = stream->recent;
  1153.   fstat (LOCAL->fd,&sbuf);    /* get status */
  1154.   if (sbuf.st_size < curpos) {    /* sanity check */
  1155.     mm_log ("Mailbox shrank!",ERROR);
  1156.     dawz_close (stream);
  1157.     return NIL;
  1158.   }
  1159.                 /* while there is stuff to parse */
  1160.   while (i = sbuf.st_size - curpos) {
  1161.                 /* get to that position in the file */
  1162.     lseek (LOCAL->fd,curpos,SEEK_SET);
  1163.     if (!((read (LOCAL->fd,tmp,64) > 0)
  1164.       && (s = strchr (tmp,'\015')) && (s[1] == '\012'))) {
  1165.       mm_log ("Unable to read internal header line",ERROR);
  1166.       dawz_close (stream);
  1167.       return NIL;
  1168.     }
  1169.     *s = '\0';            /* tie off header line */
  1170.     i = (s + 2) - tmp;        /* note start of text offset */
  1171.     if (!((s = strchr (tmp,',')) && (t = strchr (s+1,';')))) {
  1172.       mm_log ("Unable to parse internal header line",ERROR);
  1173.       dawz_close (stream);
  1174.       return NIL;
  1175.     }
  1176.     *s++ = '\0'; *t++ = '\0';    /* tie off fields */
  1177.                 /* intantiate an elt for this message */
  1178.     elt = mail_elt (stream,++nmsgs);
  1179.     elt->data1 = curpos;    /* note file offset of header */
  1180.     elt->data2 = i << 24;    /* as well as offset from header of message */
  1181.                 /* parse the header components */
  1182.     if (!(mail_parse_date (elt,tmp) &&
  1183.       (elt->rfc822_size = strtol (s,&s,10)) && (!(s && *s)) &&
  1184.       isdigit (t[0]) && isdigit (t[1]) && isdigit (t[2]) &&
  1185.       isdigit (t[3]) && isdigit (t[4]) && isdigit (t[5]) &&
  1186.       isdigit (t[6]) && isdigit (t[7]) && isdigit (t[8]) &&
  1187.       isdigit (t[9]) && isdigit (t[10]) && isdigit (t[11]) && !t[12])) {
  1188.       mm_log ("Unable to parse internal header line components",ERROR);
  1189.       dawz_close (stream);
  1190.       return NIL;
  1191.     }
  1192.                 /* update current position to next header */
  1193.     curpos += i + elt->rfc822_size;
  1194.                 /* calculate system flags */
  1195.     if ((i = ((t[10]-'0') * 8) + t[11]-'0') & fSEEN) elt->seen = T;
  1196.     if (i & fDELETED) elt->deleted = T;
  1197.     if (i & fFLAGGED) elt->flagged = T;
  1198.     if (i & fANSWERED) elt->answered = T;
  1199.     if (curpos > sbuf.st_size) {
  1200.       mm_log ("Last message runs past end of file",ERROR);
  1201.       dawz_close (stream);
  1202.       return NIL;
  1203.     }
  1204.   }
  1205.                 /* update parsed file size */
  1206.   LOCAL->filesize = sbuf.st_size;
  1207.   mail_exists (stream,nmsgs);    /* notify upper level of new mailbox size */
  1208.   mail_recent (stream,recent);    /* and of change in recent messages */
  1209.   return T;            /* return the winnage */
  1210. }
  1211.  
  1212. /* Dawz copy messages
  1213.  * Accepts: MAIL stream
  1214.  *        mailbox copy vector
  1215.  *        mailbox name
  1216.  * Returns: T if success, NIL if failed
  1217.  */
  1218.  
  1219. long dawz_copy_messages (MAILSTREAM *stream,char *mailbox)
  1220. {
  1221.   char tmp[MAILTMPLEN];
  1222.   struct stat sbuf;
  1223.   MESSAGECACHE *elt;
  1224.   unsigned long i,j,k;
  1225.   int fd;
  1226.                 /* got file? */
  1227.   if (!dawz_file (tmp,mailbox)) return dawz_badname (tmp,mailbox);
  1228.   if ((fd = open (tmp,O_BINARY|O_WRONLY|O_APPEND|O_CREAT,S_IREAD|S_IWRITE))<0){
  1229.     sprintf (tmp,"Unable to open copy mailbox: %s",strerror (errno));
  1230.     mm_log (tmp,ERROR);
  1231.     return NIL;
  1232.   }
  1233.   mm_critical (stream);        /* go critical */
  1234.   fstat (fd,&sbuf);        /* get current file size */
  1235.                 /* for each requested message */
  1236.   for (i = 1; i <= stream->nmsgs; i++)
  1237.     if ((elt = mail_elt (stream,i))->sequence) {
  1238.       lseek (LOCAL->fd,dawz_header (stream,i,&k),SEEK_SET);
  1239.       j = 0;            /* mark first time through */
  1240.       do {            /* read from source position */
  1241.     if (j) {        /* get another message chunk */
  1242.       k = min (j,(unsigned long) MAILTMPLEN);
  1243.       read (LOCAL->fd,tmp,(unsigned int) k);
  1244.     }
  1245.     else {            /* first time through */
  1246.       mail_date (tmp,elt);    /* make a header */
  1247.       sprintf (tmp + strlen (tmp),",%d;000000000000\015\012",
  1248.            elt->rfc822_size);
  1249.       k = strlen (tmp);    /* size of header string */
  1250.                 /* amount of data to write subsequently */
  1251.       j = elt->rfc822_size + k;
  1252.     }
  1253.     if (write (fd,tmp,(unsigned int) k) < 0) {
  1254.       sprintf (tmp,"Unable to write message: %s",strerror (errno));
  1255.       mm_log (tmp,ERROR);
  1256.       chsize (fd,sbuf.st_size);
  1257.       close (fd);        /* punt */
  1258.       mm_nocritical (stream);
  1259.       return NIL;
  1260.     }
  1261.       } while (j -= k);        /* until done */
  1262.     }
  1263.   close (fd);            /* close the file */
  1264.   mm_nocritical (stream);    /* release critical */
  1265.   return T;
  1266. }
  1267.  
  1268. /* Dawz update status string
  1269.  * Accepts: MAIL stream
  1270.  *        message number
  1271.  */
  1272.  
  1273. void dawz_update_status (MAILSTREAM *stream,long msgno)
  1274. {
  1275.   char tmp[MAILTMPLEN];
  1276.   MESSAGECACHE *elt = mail_elt (stream,msgno);
  1277.   unsigned long j,k = 0;
  1278.   if (stream->readonly) return;    /* not if readonly you don't */
  1279.   j = elt->user_flags;        /* get user flags */
  1280.                 /* reverse bits (dontcha wish we had CIRC?) */
  1281.   while (j) k |= 1 << 29 - find_rightmost_bit (&j);
  1282.                 /* print new flag string */
  1283.   sprintf (tmp,"%010lo%02o",k,(fSEEN * elt->seen) + (fDELETED * elt->deleted) +
  1284.        (fFLAGGED * elt->flagged) + (fANSWERED * elt->answered));
  1285.                 /* get to that place in the file */
  1286.   lseek (LOCAL->fd,(off_t) elt->data1 + (elt->data2 >> 24) - 14,SEEK_SET);
  1287.   write (LOCAL->fd,tmp,12);    /* write new flags */
  1288. }
  1289.  
  1290. /* Search support routines
  1291.  * Accepts: MAIL stream
  1292.  *        message number
  1293.  *        pointer to additional data
  1294.  * Returns: T if search matches, else NIL
  1295.  */
  1296.  
  1297.  
  1298. char dawz_search_all (MAILSTREAM *stream,long msgno,char *d,long n)
  1299. {
  1300.   return T;            /* ALL always succeeds */
  1301. }
  1302.  
  1303.  
  1304. char dawz_search_answered (MAILSTREAM *stream,long msgno,char *d,long n)
  1305. {
  1306.   return mail_elt (stream,msgno)->answered ? T : NIL;
  1307. }
  1308.  
  1309.  
  1310. char dawz_search_deleted (MAILSTREAM *stream,long msgno,char *d,long n)
  1311. {
  1312.   return mail_elt (stream,msgno)->deleted ? T : NIL;
  1313. }
  1314.  
  1315.  
  1316. char dawz_search_flagged (MAILSTREAM *stream,long msgno,char *d,long n)
  1317. {
  1318.   return mail_elt (stream,msgno)->flagged ? T : NIL;
  1319. }
  1320.  
  1321.  
  1322. char dawz_search_keyword (MAILSTREAM *stream,long msgno,char *d,long n)
  1323. {
  1324.   return NIL;
  1325. }
  1326.  
  1327.  
  1328. char dawz_search_new (MAILSTREAM *stream,long msgno,char *d,long n)
  1329. {
  1330.   MESSAGECACHE *elt = mail_elt (stream,msgno);
  1331.   return (elt->recent && !elt->seen) ? T : NIL;
  1332. }
  1333.  
  1334. char dawz_search_old (MAILSTREAM *stream,long msgno,char *d,long n)
  1335. {
  1336.   return mail_elt (stream,msgno)->recent ? NIL : T;
  1337. }
  1338.  
  1339.  
  1340. char dawz_search_recent (MAILSTREAM *stream,long msgno,char *d,long n)
  1341. {
  1342.   return mail_elt (stream,msgno)->recent ? T : NIL;
  1343. }
  1344.  
  1345.  
  1346. char dawz_search_seen (MAILSTREAM *stream,long msgno,char *d,long n)
  1347. {
  1348.   return mail_elt (stream,msgno)->seen ? T : NIL;
  1349. }
  1350.  
  1351.  
  1352. char dawz_search_unanswered (MAILSTREAM *stream,long msgno,char *d,long n)
  1353. {
  1354.   return mail_elt (stream,msgno)->answered ? NIL : T;
  1355. }
  1356.  
  1357.  
  1358. char dawz_search_undeleted (MAILSTREAM *stream,long msgno,char *d,long n)
  1359. {
  1360.   return mail_elt (stream,msgno)->deleted ? NIL : T;
  1361. }
  1362.  
  1363.  
  1364. char dawz_search_unflagged (MAILSTREAM *stream,long msgno,char *d,long n)
  1365. {
  1366.   return mail_elt (stream,msgno)->flagged ? NIL : T;
  1367. }
  1368.  
  1369.  
  1370. char dawz_search_unkeyword (MAILSTREAM *stream,long msgno,char *d,long n)
  1371. {
  1372.   return NIL;
  1373. }
  1374.  
  1375.  
  1376. char dawz_search_unseen (MAILSTREAM *stream,long msgno,char *d,long n)
  1377. {
  1378.   return mail_elt (stream,msgno)->seen ? NIL : T;
  1379. }
  1380.  
  1381. char dawz_search_before (MAILSTREAM *stream,long msgno,char *d,long n)
  1382. {
  1383.   MESSAGECACHE *elt = mail_elt (stream,msgno);
  1384.   return (char) (((elt->year << 9) + (elt->month << 5) + elt->day) < n);
  1385. }
  1386.  
  1387.  
  1388. char dawz_search_on (MAILSTREAM *stream,long msgno,char *d,long n)
  1389. {
  1390.   MESSAGECACHE *elt = mail_elt (stream,msgno);
  1391.   return (char) (((elt->year << 9) + (elt->month << 5) + elt->day) == n);
  1392. }
  1393.  
  1394.  
  1395. char dawz_search_since (MAILSTREAM *stream,long msgno,char *d,long n)
  1396. {
  1397.                 /* everybody interprets "since" as .GE. */
  1398.   MESSAGECACHE *elt = mail_elt (stream,msgno);
  1399.   return (char) (((elt->year << 9) + (elt->month << 5) + elt->day) >= n);
  1400. }
  1401.  
  1402.  
  1403. char dawz_search_body (MAILSTREAM *stream,long msgno,char *d,long n)
  1404. {
  1405.   return NIL;            /* need code here */
  1406. }
  1407.  
  1408.  
  1409. char dawz_search_subject (MAILSTREAM *stream,long msgno,char *d,long n)
  1410. {
  1411.   char *s = dawz_fetchstructure (stream,msgno,NIL)->subject;
  1412.   return s ? search (s,(long) strlen (s),d,n) : NIL;
  1413. }
  1414.  
  1415.  
  1416. char dawz_search_text (MAILSTREAM *stream,long msgno,char *d,long n)
  1417. {
  1418.   return NIL;            /* need code here */
  1419. }
  1420.  
  1421. char dawz_search_bcc (MAILSTREAM *stream,long msgno,char *d,long n)
  1422. {
  1423.   char tmp[8*MAILTMPLEN];
  1424.   tmp[0] = '\0';        /* initially empty string */
  1425.                 /* get text for address */
  1426.   rfc822_write_address (tmp,dawz_fetchstructure (stream,msgno,NIL)->bcc);
  1427.   return search (tmp,(long) strlen (tmp),d,n);
  1428. }
  1429.  
  1430.  
  1431. char dawz_search_cc (MAILSTREAM *stream,long msgno,char *d,long n)
  1432. {
  1433.   char tmp[8*MAILTMPLEN];
  1434.   tmp[0] = '\0';        /* initially empty string */
  1435.                 /* get text for address */
  1436.   rfc822_write_address (tmp,dawz_fetchstructure (stream,msgno,NIL)->cc);
  1437.   return search (tmp,(long) strlen (tmp),d,n);
  1438. }
  1439.  
  1440.  
  1441. char dawz_search_from (MAILSTREAM *stream,long msgno,char *d,long n)
  1442. {
  1443.   char tmp[8*MAILTMPLEN];
  1444.   tmp[0] = '\0';        /* initially empty string */
  1445.                 /* get text for address */
  1446.   rfc822_write_address (tmp,dawz_fetchstructure (stream,msgno,NIL)->from);
  1447.   return search (tmp,(long) strlen (tmp),d,n);
  1448. }
  1449.  
  1450.  
  1451. char dawz_search_to (MAILSTREAM *stream,long msgno,char *d,long n)
  1452. {
  1453.   char tmp[8*MAILTMPLEN];
  1454.   tmp[0] = '\0';        /* initially empty string */
  1455.                 /* get text for address */
  1456.   rfc822_write_address (tmp,dawz_fetchstructure (stream,msgno,NIL)->to);
  1457.   return search (tmp,(long) strlen (tmp),d,n);
  1458. }
  1459.  
  1460. /* Search parsers */
  1461.  
  1462.  
  1463. /* Parse a date
  1464.  * Accepts: function to return
  1465.  *        pointer to date integer to return
  1466.  * Returns: function to return
  1467.  */
  1468.  
  1469. search_t dawz_search_date (search_t f,long *n)
  1470. {
  1471.   long i;
  1472.   char *s;
  1473.   MESSAGECACHE elt;
  1474.                 /* parse the date and return fn if OK */
  1475.   return (dawz_search_string (f,&s,&i) && mail_parse_date (&elt,s) &&
  1476.       (*n = (elt.year << 9) + (elt.month << 5) + elt.day)) ? f : NIL;
  1477. }
  1478.  
  1479. /* Parse a flag
  1480.  * Accepts: function to return
  1481.  *        pointer to keyword integer to return
  1482.  *        MAIL stream
  1483.  * Returns: function to return
  1484.  */
  1485.  
  1486. search_t dawz_search_flag (search_t f,long *n,MAILSTREAM *stream)
  1487. {
  1488.   strtok (NIL," ");        /* slurp keyword */
  1489.   return f;
  1490. }
  1491.  
  1492. /* Parse a string
  1493.  * Accepts: function to return
  1494.  *        pointer to string to return
  1495.  *        pointer to string length to return
  1496.  * Returns: function to return
  1497.  */
  1498.  
  1499.  
  1500. search_t dawz_search_string (search_t f,char **d,long *n)
  1501. {
  1502.   char *c = strtok (NIL,"");    /* remainder of criteria */
  1503.   if (c) {            /* better be an argument */
  1504.     switch (*c) {        /* see what the argument is */
  1505.     case '\0':            /* catch bogons */
  1506.     case ' ':
  1507.       return NIL;
  1508.     case '"':            /* quoted string */
  1509.       if (!(strchr (c+1,'"') && (*d = strtok (c,"\"")) && (*n = strlen (*d))))
  1510.     return NIL;
  1511.       break;
  1512.     case '{':            /* literal string */
  1513.       *n = strtol (c+1,&c,10);    /* get its length */
  1514.       if (*c++ != '}' || *c++ != '\015' || *c++ != '\012' ||
  1515.       *n > strlen (*d = c)) return NIL;
  1516.       c[*n] = '\255';        /* write new delimiter */
  1517.       strtok (c,"\255");    /* reset the strtok mechanism */
  1518.       break;
  1519.     default:            /* atomic string */
  1520.       *n = strlen (*d = strtok (c," "));
  1521.       break;
  1522.     }
  1523.     return f;
  1524.   }
  1525.   else return NIL;
  1526. }
  1527.